using System;
using System.Runtime.Serialization;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using Kollektiv.Extension;

namespace Kollektiv.Config
{
	/// <summary>
	/// Configuration service.
	/// </summary>
    public static class ConfigService
    {
        private static string configDir = Directory.GetCurrentDirectory();
        private static string resourcePrefix = string.Empty;
        private static Assembly resourceAssembly;

        /// <summary>
        /// Sets the directory containing the configuration files.
        /// </summary>
        /// <param name='dir'>
        /// The directory path.
        /// </param>
        public static void SetDirectory(string dir)
        {
            configDir = dir;
        }

        public static void SetResourcePrefix(string prefix)
        {
            resourcePrefix = prefix;
        }

        /// <summary>
        /// Sets the assembly containing the configuration files as embedded ressources.
        /// This assembly resources are used as fallback configuration files if a file is not found in the set directory.
        /// </summary>
        /// <param name='assembly'>
        /// The resource assembly.
        /// </param>
        public static void SetResourceAssembly(Assembly assembly)
        {
            resourceAssembly = assembly;
        }

        /// <summary>
        /// Loads a configuration object.
        /// </summary>
        /// <returns>
        /// The configuration object.
        /// </returns>
        /// <typeparam name='T'>
        /// Type of the configuration object.
        /// </typeparam>
        public static T Load<T>() where T : class
        {
            string name = typeof(T).FullName + ".gconfig.xml";
            string path = Path.Combine(configDir, name);
            string resourceName = string.Concat(resourceAssembly.GetName().Name, ".", resourcePrefix, name);

            T retVal = null;

            if (File.Exists(path))
            {
                using(FileStream fs = File.OpenRead(path))
                {
                    retVal = fs.DeserializeDataContract<T>();
                }
            }
            else if (resourceAssembly != null && resourceAssembly.GetManifestResourceInfo(resourceName) != null)
            {
                using (Stream rs = resourceAssembly.GetManifestResourceStream(resourceName))
                {
                    retVal = rs.DeserializeDataContract<T>();
                }
            }
            else
            {
                throw (new FileNotFoundException("configfile <" + path + "> not found"));
            }

            return retVal;
        }

        /// <summary>
        /// Saves the specified configuration object.
        /// </summary>
        /// <param name='config'>
        /// The configuration object to save.
        /// </param>
        /// <typeparam name='T'>
        /// Type of the configuration object.
        /// </typeparam>
        public static void Save<T>(T config) where T : class
        {            
            string name = typeof(T).FullName + ".gconfig.xml";
            string path = Path.Combine(configDir, name);

            using (FileStream fs = File.OpenWrite(path))
            {
                config.SerializeDataContract(fs);
            }
        }
    }
	
	/// <summary>
	/// Object to store dynamic configuration values.
	/// </summary>
	[DataContract]
	public class ConfigValue
	{
		[DataMember]
		public string Key { get; set; }
		
		[DataMember]
		public string Value { get; set; }
	}
	
	public class ConfigException:Exception
	{
		public ConfigException (string message):base(message)
		{
		}
	}
}